#include "oracle_protocol.h"
#include "log.h"

namespace secsmart_protocol
{
    DETECT_RESULT OracleProtocol::parse(secsmart_session::session_sptr session, const uint8_t *buf, size_t size, bool is_forward)
    {
        direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_UNKNOW;
        if(size < HEADER_LEN)  //小于协议长度
        {
            return DETECT_RESULT::DETECT_UNKNOWN;
        }

        size_t pos = 0;
        uint32_t packetLen = 0;
        uint16_t checksum = 0;
        uint8_t packetType = 0;
        uint16_t hCheckSum = 0;

        packetLen = ((uint8_t)(buf[pos]) << 8) + ((uint8_t)(buf[pos+1]));
        checksum = ((uint8_t)(buf[pos+2]) << 8) + ((uint8_t)(buf[pos+3]));

        //判断oracle数据包packetLen读两个字节还是四个字节
        if( packetLen == 0 && checksum !=0)
        {
            packetLen = checksum;
            checksum = 0;
        }

        if(packetLen > 2097512 || packetLen < HEADER_LEN) //包长度合法性校验
        {
            addNoMatchCount();
            return DETECT_RESULT::DETECT_FAIL;
        }

        packetType = (uint8_t)(buf[pos+4]);
        if(packetType > PACKET_TYPE::TYPE_MAXID) //包类型合法性校验
        {
            addNoMatchCount();
            return DETECT_RESULT::DETECT_FAIL;
        }

        hCheckSum = ((uint8_t)(buf[pos+6]) << 8) + ((uint8_t)(buf[pos+7]));
        if(hCheckSum != 0) //hCheckSum字段合法性校验
        {
            addNoMatchCount();
            return DETECT_RESULT::DETECT_FAIL;
        }

        if(packetLen > size)   //TCP分段不识别
        {
            return DETECT_RESULT::DETECT_UNKNOWN;
        }

       else if(packetLen == size)
        {
            switch(packetType)    //根据oracle数据包的包类型确定方向
            {
                case TYPE_CONNECTION:
                {
                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_REQUEST;
                    LOG_DEBUG("Oracle: connect packet deteeted!")
                    if(!setIpPort(session, getDirction(), is_forward)){
                    LOG_DEBUG("set port ip fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                    addMatchCount();
                    return DETECT_RESULT::DETECT_OK;
                }
                case TYPE_ACCEPT:
                {
                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                    LOG_DEBUG("Oracle: accept packet deteeted!")
                    if(!setIpPort(session, getDirction(), is_forward)){
                    LOG_DEBUG("set port ip fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                    addMatchCount();
                    return DETECT_RESULT::DETECT_OK;
                }
                case TYPE_REFUSE:
                {
                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                    LOG_DEBUG("Oracle: refuse packet deteeted!")
                    if(!setIpPort(session, getDirction(), is_forward)){
                    LOG_DEBUG("set port ip fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                    addMatchCount();
                    return DETECT_RESULT::DETECT_OK;
                }
                case TYPE_REDIRECT:
                {
                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                    LOG_DEBUG("Oracle: redirect packet deteeted!")
                    if(!setIpPort(session, getDirction(), is_forward)){
                    LOG_DEBUG("set port ip fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                    addMatchCount();
                    return DETECT_RESULT::DETECT_OK;
                }
                case TYPE_RESEND:
                {
                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                    LOG_DEBUG("Oracle: resend packet deteeted!")
                    if(!setIpPort(session, getDirction(), is_forward)){
                    LOG_DEBUG("set port ip fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                    addMatchCount();
                    return DETECT_RESULT::DETECT_OK;
                }
                case TYPE_DATA:  
                {
                    //包类型为TYPE_DATA时还需要根据commandID或者callID来进一步判断确定方向
                    //部分commandID和callID类型未造出相应的包，因此直接使用魔法数字
                    uint8_t commandID = buf[pos+10];
                    uint8_t callID = buf[pos+11];   
                    switch(commandID)
                    {
                        case CmdData::cdRetSTA:
                        case CmdData::cdFuncCmplt:
                        case 0x0f:
                        case CmdData::cdDescInfo:
                        case CmdData::cdRowXH:
                        case 0x0b:
                        case 0x15:
                        case 0x0d:
                        case 0x13:
                        case 0x17:
                        case 0x19:
                        case 0x1c:
                        {
                            direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                            LOG_DEBUG("Oracle: respond packet deteeted!")
                            if(!setIpPort(session, getDirction(), is_forward)){
                            LOG_DEBUG("set port ip fail");
                            return DETECT_RESULT::DETECT_FAIL;
                        }
                            addMatchCount();
                            return DETECT_RESULT::DETECT_OK;
                        }
                        case CmdData::cdRetOPI:
                        {
                            direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                            LOG_DEBUG("Oracle: respond packet deteeted!")
                            if(!setIpPort(session, getDirction(), is_forward)){
                            LOG_DEBUG("set port ip fail");
                            return DETECT_RESULT::DETECT_FAIL;
                        }
                            if(size > 70){   
                            //此处的70是个粗略估计的数字，8字节协议头+2字节dataFlag+1字节dataID+1或者2字节unused+1字节的Banner Length
                            //+ Banner: Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
                            parseVersion(buf+11,size-11);
                            }
                            addMatchCount();
                            return DETECT_RESULT::DETECT_OK;
                        }

                        case CmdData::cdUserOCI:
                        {
                            switch(callID)
                            {
                                case CmdUserOCI::cGetSessKey:
                                case CmdUserOCI::cGAuthCall:
                                case CmdUserOCI::cO3Log1st:
                                case CmdUserOCI::cO3Log2nd:
                                case CmdUserOCI::cOrcVerNew:
                                case CmdUserOCI::cAutCmitOn:
                                case CmdUserOCI::cAutCmitOff:
                                case CmdUserOCI::cOpenCursor:
                                case CmdUserOCI::cParseARow:
                                case CmdUserOCI::cParseEXE:
                                case CmdUserOCI::cBundleEXE:
                                case CmdUserOCI::cFastOPIAL7:
                                case CmdUserOCI::cOSQL7:
                                case CmdUserOCI::cv8Dscrarr:
                                case CmdUserOCI::cBundleEXEV7:
                                case CmdUserOCI::cCommit:
                                case CmdUserOCI::cRollback:
                                case CmdUserOCI::cFetchRow:
                                case CmdUserOCI::cDscrArr:
                                case CmdUserOCI::cExeRow:
                                case CmdUserOCI::cExefetch:
                                case CmdUserOCI::cPing:
                                case 0x60:
                                case 0x68:
                                case 0xbe:
                                case CmdUserOCI::cCloseQuery:
                                case CmdUserOCI::cCloseCursor:
                                case CmdUserOCI::cLogOff:
                                {
                                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_REQUEST;
                                    LOG_DEBUG("Oracle: 0x03 request packet deteeted!")
                                    if(!setIpPort(session, getDirction(), is_forward)){
                                    LOG_DEBUG("set port ip fail");
                                    return DETECT_RESULT::DETECT_FAIL;
                                }
                                    addMatchCount();
                                    return DETECT_RESULT::DETECT_OK;
                                }
                                default:
                                {
                                    return DETECT_RESULT::DETECT_UNKNOWN;
                                }

                            }
                        }
                        case 0x1a:
                        {
                            if(callID == 0xb0)
                            {
                                direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_REQUEST;
                                LOG_DEBUG("Oracle: 0x1a request packet deteeted!")
                                if(!setIpPort(session, getDirction(), is_forward)){
                                LOG_DEBUG("set port ip fail");
                                return DETECT_RESULT::DETECT_FAIL;
                            }
                                addMatchCount();
                                return DETECT_RESULT::DETECT_OK;
                            }
                            else 
                            {
                                return DETECT_RESULT::DETECT_UNKNOWN;
                            }
                        }
                        case CmdData::cdPiggyBack:
                        {
                            switch(callID)
                            {
                                case 0x6b:
                                case 0x87:
                                case 0x9a:
                                case 0x9d:
                                case 0xa7:
                                case 0xa8:
                                case 0xb0:
                                case 0xb1:
                                case 0xbe:
                                case CmdUserOCI::cCancelAll:
                                case CmdUserOCI::cCursorCloseAll:
                                case 0x60:
                                {
                                    direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_REQUEST;
                                    LOG_DEBUG("Oracle: 0x11 request packet deteeted!")
                                    if(!setIpPort(session, getDirction(), is_forward)){
                                    LOG_DEBUG("set port ip fail");
                                    return DETECT_RESULT::DETECT_FAIL;
                                }
                                    addMatchCount();
                                    return DETECT_RESULT::DETECT_OK;
                                }
                                default:
                                {
                                    return DETECT_RESULT::DETECT_UNKNOWN;
                                }
                            }
                        }
                        default:
                        {
                            return DETECT_RESULT::DETECT_UNKNOWN;
                        }
                    }
                }
                default:
                {
                    return DETECT_RESULT::DETECT_UNKNOWN;
                }
            }
        }
        else 
        {
            return DETECT_RESULT::DETECT_UNKNOWN;
        }

    }
    std::string OracleProtocol::extendInfo()
    {
        return "";
    }

    void OracleProtocol::parseVersion(const uint8_t *buf,size_t size)
    {
        size_t pos = 0;
        bool flag = false;
        for(size_t i=0; i<size-5; i++)
        {
            if(buf[i] == 'O' && buf[i+1] == 'r' && buf[i+2] == 'a' && buf[i+3] == 'c' && buf[i+4] == 'l' && buf[i+5] =='e')
            {
                pos = i;
                flag = true;
                break;
            }
        }
        versionInfo.clear();
        if(flag)
        {
            for(size_t j = pos; j < size && buf[j] != '-'; j++)
            {
                versionInfo += buf[j];
            }
            version = versionInfo;
            LOG_DEBUG("versionInfo is {}",version);
        }
    }
 }